package simpledb.parse;

import java.util.*;
import java.io.*;

/**
 * The lexical analyzer.词法分析器
 * 
 * @author Edward Sciore
 */
public class Lexer {
  private Collection<String> keywords;
  private StreamTokenizer tok;

  /**
   * Creates a new lexical analyzer for SQL statement s.<br>
   * 为SQL语句创建一个新的语法分析器
   * 
   * @param s the SQL statement
   */
  public Lexer(String s) {
    initKeywords();
    tok = new StreamTokenizer(new StringReader(s));
    //tok.ordinaryChar('.');
    tok.lowerCaseMode(true); // ids and keywords are converted
    nextToken();
  }

  // Methods to check the status of the current token

  /**
   * Returns true if the current token is the specified delimiter character.<br>
   * 如果指定的字符分隔符是当前的token，返回true
   * 
   * @param d a character denoting the delimiter 以字符为分隔符的字符
   * @return true if the delimiter is the current token
   */
  public boolean matchDelim(char d) {
    // ttype字段将包含刚读取的标记的类型
    return d == (char) tok.ttype;
  }

  /**
   * Returns true if the current token is an integer.<br>
   * 如果当前的token整数，返回true
   * 
   * @return true if the current token is an integer
   */
  public boolean matchIntConstant() {
    return tok.ttype == StreamTokenizer.TT_NUMBER;
  }

  /**
   * Returns true if the current token is a string.
   * 
   * @return true if the current token is a string
   */
  public boolean matchStringConstant() {
    return '\'' == (char) tok.ttype;
  }

  /**
   * Returns true if the current token is the specified keyword.<br>
   * 如果当前token是指定的关键字，返回true
   * 
   * @param w the keyword string
   * @return true if that keyword is the current token
   */
  public boolean matchKeyword(String w) {
    return tok.ttype == StreamTokenizer.TT_WORD && tok.sval.equals(w);
  }

  /**
   * Returns true if the current token is a legal identifier.<br>
   * 如果当前token是合法的标识符，返回true
   * 
   * @return true if the current token is an identifier
   */
  public boolean matchId() {
    return tok.ttype == StreamTokenizer.TT_WORD && !keywords.contains(tok.sval);
  }

  // Methods to "eat" the current token

  /**
   * Throws an exception if the current token is not the specified delimiter.
   * Otherwise, moves to the next token. <br>
   * 如果当前token不是以指定的分隔符，抛出异常.否则，移动到下一个token
   * 
   * @param d a character denoting the delimiter
   */
  public void eatDelim(char d) {
    if (!matchDelim(d))
      throw new BadSyntaxException();
    nextToken();
  }

  /**
   * Throws an exception if the current token is not an integer. Otherwise,
   * returns that integer and moves to the next token. <br>
   * 如果当前token不是整数，抛出异常.否则，返回这个整数并移动到下一个token
   * 
   * @return the integer value of the current token
   */
  public int eatIntConstant() {
    if (!matchIntConstant())
      throw new BadSyntaxException();
    int i = (int) tok.nval;
    nextToken();
    return i;
  }

  /**
   * Throws an exception if the current token is not a string. Otherwise,
   * returns that string and moves to the next token. <br>
   * 如果当前token不是字符串，抛出异常.否则，返回这个字符串并移动到下一个token
   * 
   * @return the string value of the current token
   */
  public String eatStringConstant() {
    if (!matchStringConstant())
      throw new BadSyntaxException();
    String s = tok.sval; // constants are not converted to lower case
    nextToken();
    return s;
  }

  /**
   * Throws an exception if the current token is not the specified keyword.
   * Otherwise, moves to the next token. <br>
   * 如果当前token不是指定的关键字，抛出异常.否则，移动到下一个token
   * 
   * @param w the keyword string
   */
  public void eatKeyword(String w) {
    if (!matchKeyword(w))
      throw new BadSyntaxException();
    nextToken();
  }

  /**
   * Throws an exception if the current token is not an identifier. Otherwise,
   * returns the identifier string and moves to the next token. <br>
   * 如果当前token不是标识符，抛出异常.否则，返回这个标识符并移动到下一个token
   * 
   * @return the string value of the current token
   */
  public String eatId() {
    if (!matchId())
      throw new BadSyntaxException();
    String s = tok.sval;
    nextToken();
    return s;
  }

  private void nextToken() {
    try {
      tok.nextToken();
    } catch (IOException e) {
      throw new BadSyntaxException();
    }
  }

  private void initKeywords() {
    keywords = Arrays.asList("select", "from", "where", "and", "insert", "into", "values", "delete", "update", "set",
        "create", "table", "int", "varchar", "view", "as", "index", "on");
  }
}